---@class tuntian_studio
local TunTian = {}

TunTian.ReviewToDiscardPile = 20201 -- 亮出后进入弃牌堆前
TunTian.SkillReady = 20202 -- 蓄势完成
TunTian.Nullification = 20203 -- 蓄势冷却

--- 载入武将信息
---@param general General @ 武将对象
---@param spec table @ {name, reputation, illustrator, designer, cv, dead}
function TunTian.loadGeneral(general, spec)
  Fk:loadTranslationTable{
    [general.name] = spec.name or general.name,
    ["#"..general.name] = spec.reputation or "官方",
    ["illustrator:"..general.name] = spec.illustrator or "官方",
    ["designer:"..general.name] = spec.designer or "官方",
    ["cv:"..general.name] = spec.cv or "官方",
    ["~"..general.name] = spec.dead or "官方",
  }
end

--- 载入技能信息
---@param skill Skill @ 技能对象
---@param spec table @ {name, detail, voices}
function TunTian.loadSkill(skill, spec)
  Fk:loadTranslationTable{
    [skill.name] = spec.name or skill.name,
    [":"..skill.name] = spec.detail or "",
  }
  local voices = spec.voices or {}
  for i, voice in ipairs(voices) do
    Fk:loadTranslationTable{
      ["$"..skill.name..tostring(i)] = voice,
    }
  end
end

--- 载入牌的信息
--- @param card Card @ 牌对象
--- @param spec table @ 基本/锦囊 { name, des = { type, event, target, effect } } ；武器 { name, des = { type, attack_range, weapon_effect } }
function TunTian.loadCard(card, spec)
  local des = spec.des or {}
  local description = des.type
  if des.event then
    description = description .. "<br /><b>时机</b>：" .. des.event
  end
  if des.target then
    description = description .. "<br /><b>目标</b>：" .. des.target
  end
  if des.effect then
    description = description .. "<br /><b>效果</b>：" .. des.effect
  end
  if des.attack_range then
    description = description .. "<br /><b>攻击范围</b>：" .. des.attack_range
  end
  if des.weapon_effect then
    description = description .. "<br /><b>武器技能</b>：" .. des.weapon_effect
  end
  Fk:loadTranslationTable{
    [card.name] = spec.name or card.name,
    [":"..card.name] = description,
  }
end

--- 造成伤害
---@param from ServerPlayer @ 来源角色
---@param target ServerPlayer @ 目标角色
---@param damage number @ 伤害值
---@param reason string @ 原因
---@param card? Card @ 牌对象
---@return boolean @ 是否成功造成伤害
function TunTian.damage(from, target, damage, reason, card)
  return target.room:damage{
    from = from,
    to = target,
    damage = damage,
    reason = reason,
    card = card,
  }
end

--- 回复体力
---@param who ServerPlayer @ 角色
---@param num number @ 回复数量
---@param skillName string @ 技能名
---@return boolean @ 是否成功回复体力
function TunTian.recover(who, num, skillName)
  local room = who.room
  num = math.min(num, who.maxHp - who.hp)
  if num < 1 then return false end
  room:recover{
    who = who,
    num = num,
    skillName = skillName,
  }
  return true
end

--- 向其他人调整体力
--- @param player ServerPlayer @ 角色
--- @param target Player @ 目标角色
--- @param skillName string @ 技能名
--- @param value? number @ 调整值，默认为 1
function TunTian.adjustHp(player, target, skillName, value)
  local room = player.room
  value = value or 1
  local toChange = target.hp - player.hp
  if toChange > 0 then
    room:recover {
      who = player,
      num = math.min(value, toChange),
      recoverBy = player,
    }
  elseif toChange < 0 then
    room:loseHp(player, math.min(value, -toChange), skillName)
  end
end

--- 角色→Log
---@param player Player @ 角色
function TunTian.playerToLog(player)
  local color = "#0C8F0C"
  local str = '<font color="%s"><b>%s</b></font>'
  if player.general == "anjiang" and (player.deputyGeneral == "anjiang"
    or not player.deputyGeneral) then
    local ret = Fk:translate("seat#" .. player.seat)
    return string.format(str, color, ret)
  end

  local ret = player.general
  ret = Fk:translate(ret)
  if player.deputyGeneral and player.deputyGeneral ~= "" then
    ret = ret .. "/" .. Fk:translate(player.deputyGeneral)
  end
  ret = string.format(str, color, ret)
  return ret
end

-- 选场上的牌，参数：choices, all_choices, cids, fids
---@param spec table
TunTian.CardNameBox = function(spec)
  spec.choices = type(spec.choices) == "table" and spec.choices or Util.DummyTable
  spec.all_choices = type(spec.all_choices) == "table" and spec.all_choices or spec.choices
  spec.default_choice = spec.choices[1]
  spec.type = "custom"
  spec.qml_path = "packages/tuntian_studio/qml/OnfieldCardName"
  return spec
end

--- 获取卡牌数据
---@param id integer
---@param room AbstractRoom
---@return table @ id, name, suit, color, number, from (general)
TunTian.getCardData = function (id, room)
  local card = Fk:getCardById(id)
  return { id = id, name = card.name, suit = card:getSuitString(), color = card:getColorString(), number = card.number, from = room:getCardOwner(id).general }
end

--- 颜色字
---@param text string @ 文字
---@param color string @ 颜色, 如 "red", "#FFD700"
---@return string
TunTian.colorString = function (text, color)
  return string.format("<font color='%s'>%s</font>", color, text)
end

--- 超链接文字
--- @param text string @ 文字
--- @param detail string @ 具体信息
--- @return string
TunTian.hrefString = function (text, detail)
  return "<a href='"..detail.."'>"..text.."</a>"
end

-- 蓄谋 from RUtil
---@param player ServerPlayer @ 操作蓄谋的玩家
---@param card integer | Card  @ 用来蓄谋的牌
---@param skill_name? string @ 技能名
---@param proposer? integer @ 移动操作者的id
---@return nil
TunTian.premeditate = function(player, card, skill_name, proposer)
  skill_name = skill_name or ""
  proposer = proposer or player.id
  local room = player.room
  if type(card) == "table" then
    assert(not card:isVirtual() or #card.subcards == 1)
    card = card:getEffectiveId()
  end
  local xumou = Fk:cloneCard("tuntian__premeditate")
  xumou:addSubcard(card)
  room:sendLog{
    type = "#premeditate",
    from = proposer,
    to = {player.id},
  }
  player:addVirtualEquip(xumou)
  room:moveCardTo(xumou, Player.Judge, player, fk.ReasonJustMove, skill_name, nil, false, proposer, "", {proposer, player.id})
end

--- 重新隐匿 from RUtil
---@param player ServerPlayer @ 目标角色
TunTian.EnterHidden = function (player)
  local room = player.room
  room:sendLog({
    type = "#EnterHiddenAgain",
    from = player.id,
  })
  local skills = "hidden_skill&"
  room:setPlayerMark(player, "__hidden_general", player.general)
  for _, s in ipairs(Fk.generals[player.general]:getSkillNameList(true)) do
    if player:hasSkill(s, true) then
      skills = skills.."|-"..s
    end
  end
  if player.deputyGeneral ~= "" then
    room:setPlayerMark(player, "__hidden_deputy", player.deputyGeneral)
    for _, s in ipairs(Fk.generals[player.deputyGeneral]:getSkillNameList(true)) do
      if player:hasSkill(s, true) then
        skills = skills.."|-"..s
      end
    end
  end
  player.general = "hiddenone"
  player.gender = General.Male
  room:broadcastProperty(player, "gender")
  if player.deputyGeneral ~= "" then
    player.deputyGeneral = ""
  end
  player.kingdom = "jin"
  room:setPlayerMark(player, "__hidden_record",
  {
    maxHp = player.maxHp,
    hp = player.hp,
  })
  player.maxHp = 1
  player.hp = 1
  for _, property in ipairs({"general", "deputyGeneral", "kingdom", "maxHp", "hp"}) do
    room:broadcastProperty(player, property)
  end
  room:handleAddLoseSkills(player, skills, nil, false, true)
end
Fk:loadTranslationTable{ ["#EnterHiddenAgain"] = "%from 进入隐匿状态" }

-- 蓄势技 from RUtil
local ready_skill = fk.CreateActiveSkill{
  name = "tuntian__ready_skill&",
  prompt = "#tuntian__ready_skill&",
  anim_type = "special",
  interaction = function(self)
    local skills = {}
    for _, s in ipairs(Self.player_skills) do
      if Self:getMark("@@tuntian__readying:::"..s.name) > 0 and Self:usedSkillTimes(s.name, Player.HistoryRound) == 0 then
        table.insert(skills, s.name)
      end
    end
    return UI.ComboBox { choices = skills }
  end,
  can_use = function(self, player)
    return table.find(player.player_skills, function(s)
      return player:getMark("@@tuntian__readying:::"..s.name) > 0 and player:usedSkillTimes(s.name, Player.HistoryRound) == 0
    end)
  end,
  card_num = 3,
  card_filter = function(self, to_select, selected)
    return #selected < 3 and not Self:prohibitDiscard(to_select)
  end,
  target_filter = Util.FalseFunc,
  on_use = function(self, room, effect)
    local player = room:getPlayerById(effect.from)
    room:throwCard(effect.cards, self.name, player, player)
    if not player.dead then
      room:setPlayerMark(player, "@@tuntian__readying:::"..self.interaction.data, 0)
      local data = Fk.skills[self.interaction.data]
      room.logic:trigger(TunTian.SkillReady, player, data, false)
    end
  end,
}
local ready_skill_trigger = fk.CreateTriggerSkill{
  name = "#tuntian__ready_skill_trigger",
  priority = 0.001,
  global = true,

  refresh_events = {fk.EventAcquireSkill, fk.EventLoseSkill, fk.AfterSkillEffect, fk.AfterDrawPileShuffle, fk.GamePrepared},
  can_refresh = function (self, event, target, player, data)
    if event == fk.AfterDrawPileShuffle then
      return table.find(player.player_skills, function(s)
        return player:getMark("@@tuntian__readying:::"..s.name) > 0
      end)
    elseif event == fk.GamePrepared then
      -- 注意，全局触发技能添加到游戏比角色技能晚，无法检测开局添加的角色技能
      return not player:hasSkill(ready_skill.name, true) and table.find(player.player_skills, function (s)
        return s.TuntianReadySkill
      end)
    else
      return target == player and data.TuntianReadySkill
    end
  end,
  on_refresh = function (self, event, target, player, data)
    local room = player.room
    if event == fk.AfterDrawPileShuffle then
      for _, s in ipairs(player.player_skills) do
        if player:getMark("@@tuntian__readying:::"..s.name) > 0 then
          room:setPlayerMark(player, "@@tuntian__readying:::"..s.name, 0)
          room.logic:trigger(TunTian.SkillReady, player, s, false)
        end
      end
    elseif event == fk.EventAcquireSkill or event == fk.GamePrepared then
      room:handleAddLoseSkills(player, ready_skill.name, nil, false, true)
    elseif event == fk.EventLoseSkill then
      room:setPlayerMark(player, "@@tuntian__readying:::"..data.name, 0)
      if not table.find(player.player_skills, function (s)
        return s.TuntianReadySkill
      end) then
        room:handleAddLoseSkills(player, "-"..ready_skill.name, nil, false, true)
      end
    elseif event == fk.AfterSkillEffect then
      room:setPlayerMark(player, "@@tuntian__readying:::"..data.name, 1)
      room.logic:trigger(TunTian.Nullification, player, data, false)
    end
  end,
}
Fk:addSkill(ready_skill)
Fk:addSkill(ready_skill_trigger)
Fk:loadTranslationTable{
  [ready_skill.name] = "蓄势技",
  [":"..ready_skill.name] = "出牌阶段，你可以弃置三张牌，重置一个本轮未发动过的蓄势技。",
  ["#"..ready_skill.name] = "你可以弃置三张牌，重置一个本轮未发动过的蓄势技",
}

--- 改变伤害类型
---@param data table
---@param damageType integer @ 伤害类型
---@param reason string @ 原因
TunTian.changeDamageType = function (data, damageType, reason)
  data.damageType = damageType
  data.card.extra_data = data.card.extra_data or {}
  data.card.extra_data.change_reason = reason
end

--- 调整手牌数量
--- @param player ServerPlayer @ 角色
--- @param num number @ 调整到的数量
--- @param skillName string @ 技能名
TunTian.adjustHandNum = function (player, num, skillName)
  local room = player.room
  local to_change = num - #player:getCardIds("h")
  if to_change > 0 then
    player:drawCards(to_change, skillName)
  elseif to_change < 0 then
    room:askForDiscard(player, -to_change, -to_change, false, skillName, false)
  end
end

return TunTian
